/********************************************************************
* Description:  rtapi_time.c
*               This file, 'rtapi_time.c', implements the timing-
*               related functions for realtime modules.  See rtapi.h
*               for more info.
*
*     Copyright 2006-2013 Various Authors
*
*     This program is free software; you can redistribute it and/or modify
*     it under the terms of the GNU General Public License as published by
*     the Free Software Foundation; either version 2 of the License, or
*     (at your option) any later version.
*
*     This program is distributed in the hope that it will be useful,
*     but WITHOUT ANY WARRANTY; without even the implied warranty of
*     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*     GNU General Public License for more details.
*
*     You should have received a copy of the GNU General Public License
*     along with this program; if not, write to the Free Software
*     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
********************************************************************/

#include "runtime/config.h"		// build configuration
#include "runtime/rtapi.h"		// these functions
#include "runtime/rtapi_common.h"	// these functions
#include "runtime/rtapi_flavor.h"       // flavor_*

#include <time.h>		// clock_getres(), clock_gettime()


// find a useable time stamp counter
#ifdef MSR_H_USABLE
#include <asm/msr.h>
#endif

long int max_delay = DEFAULT_MAX_DELAY;

#ifdef RTAPI  /* hide most functions from ULAPI */

int period = 0;

// Actual number of counts of the periodic timer
unsigned long timer_counts;


long int rtapi_clock_set_period(long int nsecs) {
    struct timespec res = { 0, 0};

    if (nsecs == 0)
	return period;
    // 如果周期不为0, 直接返回.
    if (period != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR, "attempt to set period twice\n");
	return -EINVAL;
    }
    // 检查当前的全局 flavor 是否具有严格的实时延时
    if (flavor_feature(NULL, FLAVOR_TIME_NO_CLOCK_MONOTONIC))
        period = nsecs;
    else {
        /* 
        用于获取时钟的分辨率 (精度)
        CLOCK_MONOTONIC 特点
            单调递增：只向前走，不受系统时间调整影响
            稳定可靠：适合测量时间间隔和性能分析
            从系统启动开始：记录自系统启动以来经过的时间
        */
        clock_getres(CLOCK_MONOTONIC, &res);
        /* 
        结果 = floor( 目标值 / 基准值 ) × 基准值 
        这个操作用于时间对齐
        举例:
        nsecs = 253000;      // 请求周期：253微秒
        res.tv_nsec = 10000; // 时钟分辨率：10微秒
        period = (253000 / 10000) * 10000
            = 25 * 10000     // 253000/10000=25.3，整数除法得25
            = 250000         // 对齐到250微秒

        对齐的目的
        1. 避免累积误差
            如果周期不是分辨率的整数倍，每次定时器触发都会有微小误差，长期运行会累积。
        2. 硬件限制
            硬件定时器只能在特定的时间间隔上触发，无法在任意时间点触发。
        3. 提高精度
            确保定时器在精确的时钟刻度上触发，提高时间控制的准确性。
        */
        period = (nsecs / res.tv_nsec) * res.tv_nsec;

        if (period < 1)
            period = res.tv_nsec;

        rtapi_print_msg(RTAPI_MSG_DBG,
                        "rtapi_clock_set_period (res=%ld) -> %d\n", res.tv_nsec,
                        period);
    }

    return period;
}

/*
让当前线程睡眠 (nsec) ns的时间
*/
void rtapi_delay(long int nsec)
{
    if (nsec > max_delay) {
	nsec = max_delay;
    }
    flavor_task_delay_hook(NULL, nsec);
}

// 返回最大延迟时间
long int rtapi_delay_max(void)
{
    return max_delay;
}

#endif /* RTAPI */

/* The following functions are common to both RTAPI and ULAPI */

/* 获取当前时间 */ 
long long int rtapi_get_time(void) {
    long long int res;

    res = flavor_get_time_hook(NULL);
    if (res == -ENOSYS) { // Unimplemented
        struct timespec ts;
        // 获取单调递增的当前时间
        clock_gettime(CLOCK_MONOTONIC, &ts);
        res = ts.tv_sec * 1000000000LL + ts.tv_nsec;
    }
    return res;
}

// 返回CPU的周期数, 如果flavor_get_clocks_hook没有实现那么跟 rtapi_get_time 没区别
long long int rtapi_get_clocks(void) {
    long long int res;
    res = flavor_get_clocks_hook(NULL);
    if (res == -ENOSYS) { // Unimplemented

#     ifdef MSR_H_USABLE
        /* This returns a result in clocks instead of nS, and needs to be
           used with care around CPUs that change the clock speed to save
           power and other disgusting, non-realtime oriented behavior.
           But at least it doesn't take a week every time you call it.  */
        rdtscll(res);
#     elif defined(__i386__) || defined(__x86_64__)
        /*
        __asm__：GCC的内联汇编关键字
        __volatile__：告诉编译器不要优化这段汇编代码
        "rdtsc"：要执行的汇编指令
        "=A" (res)：输出操作数约束
        */
        __asm__ __volatile__("rdtsc" : "=A" (res));
#     else
        // Needed for e.g. ARM
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
        res = ts.tv_sec * 1000000000LL + ts.tv_nsec;
#     endif
    }
    return res;
}

#ifdef RTAPI
EXPORT_SYMBOL(rtapi_clock_set_period);
EXPORT_SYMBOL(rtapi_delay);
EXPORT_SYMBOL(rtapi_delay_max);
EXPORT_SYMBOL(rtapi_get_time);
EXPORT_SYMBOL(rtapi_get_clocks);
#endif
